package com.google.code.xconf.schema;

import com.google.code.xconf.Property;
import com.google.code.xconf.PropertyGroup;
import com.google.code.xconf.schema.definition.value.options.ExtendedBooleanModes;
import com.google.code.xconf.utils.StringTools;

import java.util.List;

public abstract class SchemaDefinition {

    protected String type;
    protected PropertyGroup definition;
    protected SchemaSettings settings;

    protected SchemaDefinition(String type, PropertyGroup definition, SchemaSettings settings) {
        this.type = type;
        this.settings = settings;
        this.definition = definition;
        if (!StringTools.equals(type, definition.getName())) {
            throw new SchemaLayoutException("Invalid group '" + definition.getName() + "', expected '" + type + "'");
        }
        validateNodes(getValidNodeNames());
    }

    protected void validateNodes(List<String> allowedNodeNames) throws SchemaLayoutException {
        for (Property property : definition.getNodes()) {
            if (!allowedNodeNames.contains(property.getName())) {
                throw new SchemaLayoutException("Invalid node-name '" + property.getName() + "'");
            }
        }
    }

    protected abstract List<String> getValidNodeNames();

    public boolean isPropertyIdentifierCaseSensitive() {
        return settings.isPropertyIdentifierCaseSensitive();
    }

    public boolean isPropertyOptional() {
        return settings.isPropertyOptional();
    }

    public boolean isPropertyStrictOrdering() {
        return settings.isPropertiesStrictOrdering();
    }

    public boolean isPropertyValueCaseSensitive() {
        return settings.isPropertyValueCaseSensitive();
    }

    public boolean isSameNamedGroupsAllowed() {
        return settings.isSameNamedGroupsAllowed();
    }

    public boolean isSameNamedPropertiesAllowed() {
        return settings.isSameNamedPropertiesAllowed();
    }

    protected Boolean resolveDefinitionPropertyBoolean(String id, boolean optional, boolean defaultValue) {
        String value = resolveDefinitionPropertyString(id, optional);
        if (value != null) {
            if (!new ExtendedBooleanModes().matches(value, false)) {
                throw new SchemaLayoutException("Property '" + id  + "' value should be of type boolean");
            }
            return ExtendedBooleanModes.isTrue(value, false);
        }
        if (!optional) {
            throw new SchemaLayoutException("Property '" + id + "' missing");
        }
        return defaultValue;
    }

    protected String resolveDefinitionPropertyString(String id, boolean optional) {
        if (!definition.containsProperty(id)) {
            if (!optional) {
                throw new SchemaLayoutException("Missing property '" + id + "'");
            }
            return null;
        }
        if (definition.getProperties(id).size() > 1) {
            throw new SchemaLayoutException("Property '" + id + "' can only be assigned once");
        }
        Property property = definition.getFirstProperty(id);
        if (!property.hasValues()) {
            throw new SchemaLayoutException("Property '" + id + "' cannot be empty");
        }
        if (property.numValues() > 1) {
            throw new SchemaLayoutException("Property '" + id + "' cannot contain more than one value");
        }
        return property.firstValue();
    }

}
